package com.xnx3.weixin.applet.login.controller;

import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.xnx3.Lang;
import com.xnx3.banmahuishou.core.util.AppUtil;
import com.xnx3.j2ee.controller.BaseController;
import com.xnx3.j2ee.entity.User;
import com.xnx3.j2ee.service.SqlCacheService;
import com.xnx3.j2ee.service.SqlService;
import com.xnx3.j2ee.service.UserService;
import com.xnx3.j2ee.util.ActionLogUtil;
import com.xnx3.j2ee.vo.BaseVO;
import com.xnx3.weixin.applet.Global;
import com.xnx3.weixin.applet.login.entity.WeixinAppletUserOpenid;
import com.xnx3.weixin.applet.login.vo.LoginVO;
import com.xnx3.weixin.vo.Jscode2sessionResultVO;
import com.xnx3.weixin.vo.PhoneVO;

import net.sf.json.JSONObject;

/**
 * 注册登录相关控制器
 * @author 管雷鸣
 */
@Controller(value="WMPluginWeixinAppletLoginController")
@RequestMapping("/weixin/applet/login/")
public class LoginController extends BaseController{
	@Resource
	private UserService userService;
	@Resource
	private SqlCacheService sqlCacheService;
	@Resource
	private SqlService sqlService;
	
	/**
	 * 通过 code(openid) 自动登陆 。
	 * <p>用到微信小程序的 wx.login 进行获取code</p>
	 * <p>1. 微信获取随机code，传入此处； </p>
	 * <p>2. 服务器通过code拉取用户 unionid；  </p>
	 * <p>3. 利用 unionid 来判断用户是否存在及设置用户自动登陆状态</p>
	 * @param code 微信小程序获取的随机code
	 * @param storeid 要登录哪个商铺，也就是要使用哪个商铺的微信设置的参数
	 * @param referrerid 推荐人id，这里传入的是推荐人的 user.id 这个在创建用户信息时，会跟storeid一块计入用户的 StoreUser.referrerid。 可不填
	 * @return {@link LoginVO} 登陆结果。根据getResult() 进行判断
	 * 		<ul>
	 * 			<li> {@link LoginVO}.getResult() == {@link LoginVO}.SUCCESS : 自动登陆成功。 同时，有一种情况，用户第一次登陆，也就是刚注册时，getInfo() 会返回字符串 "reg" </li>
	 * 			<li>{@link LoginVO}.getResult() == {@link LoginVO}.FAILURE : 自动登陆失败 ，可用 getInfo() 获取失败原因</li>
	 * 		</ul>
	 * @author 管雷鸣
	 */
	@RequestMapping(value="loginByOpenid.json",method = {RequestMethod.POST})
	@ResponseBody
	public LoginVO loginByCode(HttpServletRequest request,
			@RequestParam(value = "code", required = false, defaultValue="") String code){
		LoginVO vo = new LoginVO();
		
		//判断当前用户是否已经登录过了
		if(getUserId() > 0){
			//已登录过了
			vo.setUser(getUser());
			vo.setInfo("您已经是登录状态了，所以不再返回 userWeixin 字段了。只有未登录状态下，登录成功，才会返回 userWeixin");
			return vo;
		}
		if(code.length() == 0){
			vo.setBaseVO(LoginVO.FAILURE, "请传入您的微信小程序获取到的code");
			return vo;
		}
		if(!Global.isUse()) {
			vo.setBaseVO(LoginVO.FAILURE, "微信小程序未配置，请在 applilcation.properties 中配置");
			return vo;
		}
		
		Jscode2sessionResultVO jvo = Global.weixinAppletUtil.loginByCode(code);
		if(jvo.getResult() - Jscode2sessionResultVO.SUCCESS == 0){
			//登陆成功
			vo.setSessionKey(jvo.getSessionKey());
			
			WeixinAppletUserOpenid weixinAppletUserOpenid = sqlCacheService.findAloneByProperty(WeixinAppletUserOpenid.class, "openid", jvo.getOpenid());
			User user = null;
			if(weixinAppletUserOpenid == null){
				//此用户还未注册，进行注册用户
				user = new User();
				user.setUsername(jvo.getOpenid());
				user.setPassword(Lang.uuid());
				user.setNickname("nickname");
				user.setDailishangUserid(0);
				user.setWanggeUserid(0);
				user.setGrade("一级");
				user.setLotteryDrawCount(0);
				// 添加邀请人用户ID
				String parameter = request.getParameter("userid");
				if (parameter != null) {
					boolean b = AppUtil.isInteger(parameter);
					if (b) {
						user.setReferrerid(Integer.valueOf(parameter));
					}
				}
				BaseVO regVO = userService.reg(user, request);
				if(regVO.getResult() - BaseVO.FAILURE == 0){
					//自动注册失败
					vo.setBaseVO(regVO);
					return vo;
				}
				
				//自动注册成功，保存 WeixinAppletUserOpenid 对应表
				weixinAppletUserOpenid = new WeixinAppletUserOpenid();
				weixinAppletUserOpenid.setUserid(user.getId());
				weixinAppletUserOpenid.setOpenid(jvo.getOpenid());
				sqlService.save(weixinAppletUserOpenid);
				
				vo.setUser(getUser());
				vo.setInfo("reg");
				vo.setWeixinAppletUserOpenid(weixinAppletUserOpenid);
				
				ActionLogUtil.insertUpdateDatabase(request, user.getId(), "微信小程序自动注册用户: user:"+user.toString()+", openid:"+jvo.getOpenid());
			}else{
				//用户已经注册过了
				//设置当前用户为登陆的状态
				BaseVO loginVO = userService.loginForUserId(request, weixinAppletUserOpenid.getUserid());
				if(loginVO.getResult() - BaseVO.FAILURE == 0){
					vo.setBaseVO(LoginVO.FAILURE, loginVO.getInfo());
					return vo;
				}
				
				user = getUser();
				vo.setUser(user);
				vo.setWeixinAppletUserOpenid(weixinAppletUserOpenid);
				
				ActionLogUtil.insertUpdateDatabase(request, user.getId(), "微信小程序登录,"+user.toString());
			}
			
		}else{
			// jscode2session 获取失败
			vo.setBaseVO(BaseVO.FAILURE, jvo.getInfo());
		}
		
		vo.setToken(request.getSession().getId());
		return vo;
	}

	
	
	/**
	 * 获取用户手机号
	 * @param sessionKey 用户通过小程序code登录成功后获得的sessionKey
	 * @param encryptedData 小程序通过 open-type=getPhoneNumber 按钮获取的手机号加密信息
	 * @param iv 小程序通过 open-type=getPhoneNumber 按钮获取的
	 * @return 手机号。如果 {@link PhoneVO}.getResult 为 PhoneVO.SUCCESS ，那么便是获取成功，可以通过 {@link PhoneVO#getPhone()}获取手机号
	 * @author 管雷鸣
	 */
	@RequestMapping(value="getPhone.json",method = {RequestMethod.POST})
	@ResponseBody
	public PhoneVO getPhone(HttpServletRequest request,
			@RequestParam(value="sessionKey", required = true, defaultValue="") String sessionKey,
			@RequestParam(required = true, defaultValue="") String encryptedData,
			@RequestParam(required = true, defaultValue="") String iv){
		
		PhoneVO vo = new PhoneVO();
		User user = sqlService.findById(User.class, getUserId());
    	//被加密的数据
		byte[] dataByte = Base64.getDecoder().decode(encryptedData);
    	//加密秘钥
    	byte[] keyByte = Base64.getDecoder().decode(sessionKey);
    	// 偏移量
    	byte[] ivByte = Base64.getDecoder().decode(iv);
    	try {
    		//如果密钥不足16位，那么就补足.  这个if 中的内容很重要
    		int base = 16;
    		if (keyByte.length % base != 0) {
				int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
				byte[] temp = new byte[groups * base];
				Arrays.fill(temp, (byte) 0);
				System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
				keyByte = temp;
    		}
			// 初始化
			if (Security.getProvider(org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME) == null){
				Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
			}

			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
			AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
			parameters.init(new IvParameterSpec(ivByte));
			cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
			byte[] resultByte = cipher.doFinal(dataByte);
			if (null != resultByte && resultByte.length > 0) {
				String result = new String(resultByte, "UTF-8");
				JSONObject json = JSONObject.fromObject(result); 
				if(json.get("phoneNumber") != null){
					vo.setPhone(json.getString("phoneNumber"));
					if (user != null) {
						user.setPhone(json.getString("phoneNumber"));
						sqlService.save(user);
					}
				}
				if(json.get("countryCode") != null){
					vo.setCountryCode(json.getString("countryCode"));
				}
				vo.setResult(PhoneVO.SUCCESS);
				return vo;
			}else{
				vo.setBaseVO(PhoneVO.FAILURE, "weixin server response is null");
	    		return vo;
			}
    	} catch (Exception e) {
    		e.printStackTrace();
    		vo.setBaseVO(PhoneVO.FAILURE, e.getMessage());
    		return vo;
    	}
		
	}
}
